home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 4
/
Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso
/
Development
/
Source
/
Watch Volume Mount
/
WatchMount.cc
< prev
next >
Wrap
Text File
|
1994-03-08
|
10KB
|
284 lines
/*
***********************************************************************
*
* MountVol interception to find and display a MOTD file
*
* The purpose of this stuff is to detect when a new volume is being
* mounted (in any possible way: by inserting a floppy, using the Chooser, or
* clicking on the alias). If a new volume is detected, we check to see if
* there is a Message-of-the-day file at some "standard" location on that volume.
* If there is, we get the TeachTech (or whatever other application-creator
* of the MOTD file) to show the file to the user.
* At the start-up, we do the similar MOTD seeking thing for each mounted volume.
*
* The way we go about this, is patching the _MountVol system trap. The _MountVol
* is entered whenever the system needs to mount a volume. Our patch saves
* the driver ref number for the drive that contains the volume. Our main
* application dozes in the background mode, but wakes up from time to time
* to check to see if a new volume has been mounted since the last check.
* If it has, the application gets the TeachTech to show the MOTD file
* (if it can be located, of course).
*
* The path to the MOTD file is expected to be specified as a 'STR ' resource
* named 'MOTD Path'. Note, the path name should be a partial file name: for
* obvious reasons, the path name to the MOTD file should not contain the
* volume name (because it is searched on several volumes). So, the path
* name should always start with a colon, for example
* :PUBLIC:Notices:Motd.MAC
* Make sure that the TeachTech is the creator of the MOTD file (you can use
* any other editor/viewer, if you wish so).
*
***********************************************************************
*/
/* MacHeaders Included */
#include "myenv.h"
#include <string.h>
#include <AppleEvents.h>
/*
*----------------------------------------------------------------------
* Installing our _MountVol interceptor
* Note, the interceptor is a CODE resource, compiled separately. It
* is assumed to be put into the resource fork of the application. See
* the file "MountVol init.c" for more details.
* Note, the resource has to be loaded in the system heap, and be protected
* and locked from the outset (because it is a system "patch").
*
* Attention! Don't use 'NSetTrapAddress()' to set up the trap address.
* It works, but it does more than it's supposed to: besides setting the trap
* address to the new trap handler (our interceptor), it sets some flags in
* the process context. So, when the process manager backgrounds our application, it
* _RESTORES_ all the system traps the application has changed to their
* default values! But we need our interceptor to run even when the application
* is in the background!
*/
// Sets up a new handler for the specified
// system trap.
// It's a brute force method (messing directly
// with the system trap table), but it seems to
// be the only way to be sure the handler we
// installed sticks (even when the application
// is switched to the background).
static void my_set_system_trap(ProcPtr handler, const int trap_word)
{
assure( trap_word & 0x8ff, "The trap word looks like a toolbox, not a system trap");
ProcPtr * dispatch_table = (ProcPtr *)0x400; // System Trap dispatch table
dispatch_table[trap_word & 0xff] = handler;
// Make sure we really have set the trap
assert( (ProcPtr)NGetTrapAddress(trap_word & 0xff,OSTrap) == handler );
}
// "Identification" of our interceptor
// (code resource)
#define Interceptor_type 'Init' // Code resource type
#define Interceptor_name "\pMyMountVol" // Code resource name
#define Interceptor_sig 23547 // Interceptor's signature
#define _MountVol 0xA00F // _MountVol trap
struct MyMountVolLayout
{
short bra_instr; // Entry point: jump around data instruction
short signature; // To make sure it is what it is
short driver_ref_no; // Intercepted driver ref number
ProcPtr real_trap_handler; // Ptr to the real trap program
}; // The rest is the code for the "interception", of
// no interest to us here
class MountVolIntercept
{
MyMountVolLayout ** interceptor;
short last_mounted_drive; // Drive ref number for the last mounted drive
// detected
public:
MountVolIntercept(void);
~MountVolIntercept(void);
Boolean has_caught(void);
short last_driver_ref_no(void) const { return last_mounted_drive; }
};
// Install the interceptor
MountVolIntercept::MountVolIntercept(void)
{
// Load our interceptor and make sure we
// got what we need
interceptor = (MyMountVolLayout **)Get1NamedResource(Interceptor_type,Interceptor_name);
assert( interceptor != nil );
int interceptor_attr = GetResAttrs((Handle)interceptor);
do_well( ResError() );
assert( interceptor_attr & resSysHeap ); // Make sure the resource is locked,
assert( interceptor_attr & resLocked ); // protected, and in the sytem heap
assert( interceptor_attr & resProtected );
assert( (*interceptor)->signature == Interceptor_sig );
// Since we're going to deinstall the interceptor
// when we quit, we don't need to detach the resource
(*interceptor)->real_trap_handler = (ProcPtr)NGetTrapAddress(_MountVol & 0xff,OSTrap);
my_set_system_trap((ProcPtr)*interceptor,_MountVol);
last_mounted_drive = 0;
}
// Uninstall the interceptor and clean up
// after ourselves
MountVolIntercept::~MountVolIntercept(void)
{
assert( interceptor != 0 && *interceptor != 0 );
my_set_system_trap((*interceptor)->real_trap_handler,_MountVol);
ReleaseResource((Handle)interceptor);
// DisposeHandle((Handle)interceptor); <-- use this only if resource was detached
notify_and_wait("Quitting: _MountVol trap is restored");
}
// Check to see if the interceptor has caught
// smth. If it has, save the intercepted drive
// ref number.
Boolean MountVolIntercept::has_caught(void)
{
short& caught_ref_no = (*interceptor)->driver_ref_no;
if( caught_ref_no == 0 )
return FALSE;
last_mounted_drive = caught_ref_no;
caught_ref_no = 0;
return TRUE;
}
/*
*----------------------------------------------------------------------
* A class that takes care of displaying the message
* of the day file
* First, we try to locate the file at the "standard" location on a given
* volume. If we got it, we use the 'open_selection()' function to "double-click"
* on the file by sending a message to the Finder. He does the rest.
*/
class DisplayMOTD
{
StringPtr * path_name_handle;
FSSpec motd_fsspec;
public:
DisplayMOTD(void);
~DisplayMOTD(void) {}
void display_on_volume(const short drive_ref_no);
};
// Figure out the "standard" location of the
// MOTD file (from the 'STR ' resource)
DisplayMOTD::DisplayMOTD(void)
{
const unsigned char * path_rsrc_name = "\pMOTD Path";
path_name_handle = (StringPtr *)Get1NamedResource('STR ',path_rsrc_name);
assert( path_name_handle != nil );
assure( (*path_name_handle)[1] == ':', "The 'MOTD Path' should begin with a colon");
}
void open_selection(const FSSpec& file_spec);
// Find the MOTD file on a given volume and
// "open_select" it.
void DisplayMOTD::display_on_volume(const short drive_ref_no)
{
Str255 volume_name;
short vRefNum;
long free_bytes;
OSErr err;
if( (err = GetVInfo(drive_ref_no,volume_name,&vRefNum,&free_bytes)) != noErr )
{
notify("Error %d getting the volume info for the drive #%d",err,drive_ref_no);
return;
}
if( FSMakeFSSpec(vRefNum,0,*path_name_handle,&motd_fsspec) != noErr )
{
// notify("MOTD file was not found on the volume %#s",volume_name);
return;
}
open_selection(motd_fsspec);
}
/*
*----------------------------------------------------------------------
* Where everything happens - THE event loop
*/
// Handle a Quit event
// For now, pretend that we handled it
// and everything went fine
static pascal OSErr Handle_AppleEvent_quit
(AppleEvent * event, AppleEvent * reply, long refcon)
{
return noErr;
}
// Check out already mounted volumes to see
// if they contain MOTD
// Note drive_ref_numbers for mounted volumes
// don't have to be consecutive
static void check_already_mounted_vols(DisplayMOTD& motd_object)
{
const int max_mounted_vols = 10; // Check first few drives
register int drive_ref_no;
Str255 volume_name;
short vRefNum;
long free_bytes;
OSErr err;
for(drive_ref_no=1; drive_ref_no<=max_mounted_vols; drive_ref_no++)
if( (err=GetVInfo(drive_ref_no,volume_name,&vRefNum,&free_bytes)) == noErr )
motd_object.display_on_volume(drive_ref_no);
}
void main(void)
{
Initialize_MAC();
// Claim that we can handle a Quit event
do_well( AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
(ProcPtr)Handle_AppleEvent_quit,0,FALSE) );
DisplayMOTD motd_object;
check_already_mounted_vols(motd_object);
// motd_object.display_on_volume(3);
// notify_and_wait("Done");
MountVolIntercept intercept_object;
const long event_timeout = 20;
EventRecord theEvent;
for(;;)
{
WaitNextEvent(everyEvent, &theEvent,event_timeout,nil);
switch (theEvent.what)
{
case nullEvent:
if( intercept_object.has_caught() )
motd_object.display_on_volume(intercept_object.last_driver_ref_no());
continue;
case keyDown: // Terminate if any key was pressed
break;
case 23: // it is nothing but 'kHighLevelEvent'
OSErr err;
if( (err=AEProcessAppleEvent(&theEvent)) == noErr )
break; // It was a Quit event - the only one which
else if( err != errAEEventNotHandled ) // could be handled
notify("Error %d in processing an Apple Event",err);
continue; // NotHandled events are just ignored
default: // Ignore all other events
continue;
}
break;
}
}